#include "serialport.h"

#include "data/mycachedata.h"
#include "data/myfunc.h"
#include "data/hashmap.h"
#include "data/receivedata.h"
#include "serialportthread.h"
#include "script/MyScript.h"

#include <QDateTime>
#include <QtGlobal>
#include <QDebug>

#ifdef ANDROID
#include <QtAndroidExtras/QAndroidJniObject>
#include <QAndroidJniEnvironment>
#include<unistd.h>
#endif

float  SerialPort::FLIMIT = float(0.0000001);
//
SerialPort::SerialPort(int _gatherID
                       , QString _name
                       , SerialPortArg _spArg
                       , ProtocolDef _pdArg
                       , QList<PInfo> _pinfos
                       , QObject *parent)
    : QObject(parent)
    , mycachedata(NULL)
#ifndef ANDROID
    , serial(NULL)
#else
    , serialState(false)
#endif
    , serialThread(NULL)
    , myScr(NULL)
    , gatherID(_gatherID)
    , pdArg(_pdArg)
    , pinfos(_pinfos)
    , oldDownCmd("")
{
    mycachedata = MyCacheData::getInstance();
    serialconfig.name = _name;
    serialConf(_spArg);
    ptr_ReceiveData = ReceiveData::getInstance();
    myScr = new MyScript(pdArg.jsfile);
    CMDLib _cmds;
    allCallCmdInit(_cmds);
    _cmds.time_interval = _pdArg.totalcallsleep;
    pinfoTag();
#ifndef ANDROID
    serial = new QSerialPort(this);

    qRegisterMetaType<QSerialPort::SerialPortError>("QSerialPort::SerialPortError");
    connect(serial, static_cast<void (QSerialPort::*)(QSerialPort::SerialPortError)>(&QSerialPort::error),
            this, &SerialPort::handleError);
#endif
    serialThread = new SerialPortThread(_cmds,this);
    connect(serialThread, &SerialPortThread::sendMsg, this, &SerialPort::sendMsg);
    connect(serialThread, &SerialPortThread::TimeUp, this, &SerialPort::TimeUp);
#ifndef ANDROID
//    connect(serial, &QSerialPort::readyRead, serialThread, &SerialPortThread::cacheForReady);
    connect(serial, &QSerialPort::readyRead, this, &SerialPort::readData);
#endif
    serialThread->start();
    msganalysisThread = new MsgAnalysisThread(this);
    msganalysisThread->start();
    m_PortState.PortStateTime = mycachedata->getSec()+100;
}

SerialPort::~SerialPort()
{
    m_PortState.m_bExit=true;
    closeSerialPort();    
}

void SerialPort::downControl(int _exetype, int _type, int _pid, float _val)
{
    qInfo()<<QString("downControl(%1):exetype(%2),type(%3),pid(%4),val(%5)")
              .arg(gatherID).arg(_exetype).arg(_type).arg(_pid).arg(_val,0,'f',3);
    QMap<int,PInfo>::iterator it = id_to_addrs.find(_pid);
    if(it==id_to_addrs.end()){
        return;
    }
    if (_pid < 0)
    {
        controlGState(_exetype, _type, _pid, _val);
    }
    int allYXState = -1;
    if (1 == pdArg.allYXState)
    {
        allYXState = getAllYXVal(pdArg.YXGetValStart, pdArg.YXGetValEnd);
        qInfo() << QString("SerialPort downControl time(%1), gatherID(%2), allYXState=%3")
                    .arg(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss.zzz"))
                    .arg(gatherID).arg(allYXState);
    }
    QScriptValueList args;
    args<<_exetype << _type << it.value().addr << _val << allYXState;
    QString retVal = "";
    try{
        myScr_Mutex.lock();
        retVal = myScr->myFunc("getDownControl",args).toString();
        myScr_Mutex.unlock();
        qInfo() << "retVal="<<retVal;
    }catch(...){
        qWarning() << "call js do func getDownControl exp";
    }

    if(!retVal.isEmpty()){
        QStringList retList = retVal.split(',', QString::SkipEmptyParts);
        if(2!=retList.size()){
            qWarning() << QString("js to code inf(%1,%2,%3,%4) is error")
                        .arg(_exetype).arg(_type).arg(_pid).arg(_val,0,'f',2);
            return;
        }
        QString _cmd = retList.at(0);
        int checktype = retList.at(1).toInt();
        switch (checktype)
        {
        case 1:
        {
            unsigned char buf[256] = { 0 };
            int rlen = PFunc::string2bytes(_cmd.toStdString().c_str(), buf, _cmd.length());
            unsigned int crc = PFunc::crc16(buf, rlen);
            sprintf((char*)buf, "%s%02X%02X", _cmd.toStdString().c_str(), (crc & 0XFF), ((crc >> 8) & 0XFF));
            _cmd = QString::fromLocal8Bit((const char*)buf,(int)strlen((char*)buf));
        }
            break;
        default:
            break;
        }
        serialThread->AddMsg(_cmd,true,false);
        if (1== pdArg.ykSetVal&&1==_type)
        {
            setVal(it.value().addr, _val);
            setWDSVal(_pid, OnYX, _val);
        }
        qInfo() << QString("SerialPort downControl time(%1), gatherID(%2), getDownControl success, CMD(%3)")
                    .arg(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss.zzz"))
                    .arg(gatherID).arg(_cmd);
    }else{

    }
}

void SerialPort::controlGState(int _exetype, int _type, int _pid, float _val)
{
    qInfo() << QString("controlGState(%1,%2,%3,%4)")
                .arg(_exetype).arg(_type).arg(_pid).arg(_val,0,'f',2);
    ExeType _etype = (ExeType)_exetype;
    switch (_etype)
    {
    case OnGet:
    {
        switch (_pid)
        {
        case -1:
            setWDSVal(-1, OnYX, m_PortState.m_bExit ? float(0.0) : float(1.0));
            break;
        case -2:
            setWDSVal(-2, OnYX, m_PortState.m_wf ? float(1.0) : float(0.0));
            break;
        case -3:
            setWDSVal(-3, OnYX, m_PortState.m_rf ? float(1.0) : float(0.0));
            break;
        case -4:
            setWDSVal(-4, OnYX, m_PortState.m_tc ? float(1.0) : float(0.0));
            break;
        default:
            break;
        }
    }
        break;
    case OnSet:
    {
        switch (_pid)
        {
        case -4:
            m_PortState.m_tc = _val > 0 ? true : false;
            serialThread->setAllCall(m_PortState.m_tc);
            setWDSVal(-4, OnYX, m_PortState.m_tc ? float(1.0) : float(0.0));
            break;
        default:
            break;
        }
    }
        break;
    case OnTran:
        break;
    case ExeTDef:
        break;
    default:
        break;
    }
}

int SerialPort::getAllYXVal(int _start, int _end)
{
    int val = 0;
    QMap<int,PInfo>::iterator it = addr_to_ids.begin();
//    std::map<int, PInfo>::iterator it = addr_pid_pinfos.begin();
    while (it!= addr_to_ids.end())
    {
        if (it.value().type == OnYX && it.key()>=_start && it.key()<=_end)
        {
            val += ((it.value().value>0?1:0) << (it.key()-_start));
        }
        it++;
    }
    return val;
};

void SerialPort::serialConf(SerialPortArg _spArg)
{
    serialconfig.readCount = _spArg.readCount;
    serialconfig.readSleep = _spArg.readSleep;
    serialconfig.timePush = _spArg.timePush;
    switch(_spArg.BaudRate){
    case 9600:
        serialconfig.baudRate = QSerialPort::Baud9600;
        serialconfig.stringBaudRate = "9600";
        break;
    case 19200:
        serialconfig.baudRate = QSerialPort::Baud19200;
        serialconfig.stringBaudRate = "19200";
        break;
    case 38400:
        serialconfig.baudRate = QSerialPort::Baud38400;
        serialconfig.stringBaudRate = "38400";
        break;
    case 115200:
        serialconfig.baudRate = QSerialPort::Baud115200;
        serialconfig.stringBaudRate = "115200";
        break;
    default:
        serialconfig.baudRate = QSerialPort::Baud9600;
        serialconfig.stringBaudRate = "9600";
        break;
    }
    //
    switch (_spArg.DataBit) {
    case 5:
        serialconfig.dataBits = QSerialPort::Data5;
        serialconfig.stringDataBits = "5";
        break;
    case 6:
        serialconfig.dataBits = QSerialPort::Data6;
        serialconfig.stringDataBits = "6";
        break;
    case 7:
        serialconfig.dataBits = QSerialPort::Data7;
        serialconfig.stringDataBits = "7";
        break;
    case 8:
        serialconfig.dataBits = QSerialPort::Data8;
        serialconfig.stringDataBits = "8";
        break;
    default:
        serialconfig.dataBits = QSerialPort::Data8;
        serialconfig.stringDataBits = "8";
        break;
    }
    //
    if("N"==_spArg.Parity)
    {
        serialconfig.parity = QSerialPort::NoParity;
        serialconfig.stringParity = QObject::tr("None");
    }else if("E"==_spArg.Parity){
        serialconfig.parity = QSerialPort::EvenParity;
        serialconfig.stringParity = QObject::tr("Even");
    }else if("O"==_spArg.Parity){
        serialconfig.parity = QSerialPort::OddParity;
        serialconfig.stringParity = QObject::tr("Odd");
    }else if("M"==_spArg.Parity){
        serialconfig.parity = QSerialPort::MarkParity;
        serialconfig.stringParity = QObject::tr("Mark");
    }else if("S"==_spArg.Parity){
        serialconfig.parity = QSerialPort::SpaceParity;
        serialconfig.stringParity = QObject::tr("Space");
    }else{
        serialconfig.parity = QSerialPort::NoParity;
        serialconfig.stringParity = QObject::tr("None");
    }
    //
    switch (_spArg.StopBit) {
    case 1:
        serialconfig.stopBits = QSerialPort::OneStop;
        serialconfig.stringStopBits = "1";
        break;
    case 2:
        serialconfig.stopBits = QSerialPort::TwoStop;
        serialconfig.stringStopBits = "2";
        break;
    default:
        serialconfig.stopBits = QSerialPort::OneStop;
        serialconfig.stringStopBits = "1";
        break;
    }
}

void SerialPort::allCallCmdInit(CMDLib &_cmds)
{
    QScriptValueList args;
    myScr_Mutex.lock();
    QString retVal = myScr->myFunc("getTotalCallCMD",args).toString();
    myScr_Mutex.unlock();
    if(!retVal.isEmpty()){
        QStringList cmdsDesc = retVal.split(';',QString::SkipEmptyParts);
        for(int i=0; i<cmdsDesc.size(); i++){
            QStringList cmds = cmdsDesc.at(i).split(',',QString::SkipEmptyParts);
            if(2==cmds.size()){
                int type = cmds[1].trimmed().toInt();
                switch(type){
                case 0:
                    _cmds.cmd_totalcall.append(cmds[0].trimmed());
                    break;
                case 1:
                {
                    QString _cmd = cmds[0].trimmed();
                    //CRC校验
                    unsigned char bufCRC[256] = { 0 };
                    int rlen = PFunc::string2bytes(_cmd.toStdString().c_str(), bufCRC, _cmd.length());
                    unsigned int crc = PFunc::crc16(bufCRC, rlen);
                    unsigned char bufCMD[256] = { 0 };
                    sprintf((char*)bufCMD, "%s%02X%02X", _cmd.toStdString().c_str(), (crc & 0XFF), ((crc >> 8) & 0XFF));
                    _cmd = QString::fromLocal8Bit((const char*)bufCMD);
                    _cmds.cmd_totalcall.append(_cmd);
                }
                    break;
//                case 2:
//                    break;
                default:
                    _cmds.cmd_totalcall.append(cmds[0].trimmed());
                    break;
                }
            }
        }
    }
}

void SerialPort::pinfoTag()
{
    for(QList<PInfo>::iterator it = pinfos.begin(); it!=pinfos.end(); it++){
        addr_to_ids[it->addr] = *it;
        id_to_addrs[it->id] = *it;
    }
}

void SerialPort::setVal(int index, float val)
{
    QMap<int,PInfo>::iterator it = addr_to_ids.find(index);
    if (it != addr_to_ids.end())
    {
        float _val_ = val;
        if (it.value().type==OnYX)
        {
            _val_ = _val_ > 0 ? float(1) : float(0);
            //printf("setValue:%d,%.2f\n", _addr, _val_);
        }
        bool sendf = false;
        m_Map_Mutex.lock();
        it.value().updateTime = QDateTime::currentSecsSinceEpoch();
        //变化上送或间隔upInterval秒上送
        if (qAbs(it.value().value - _val_)>FLIMIT
            || (it.value().sendTime + it.value().upInterval)<it.value().updateTime)
        {
            it.value().sendTime = it.value().updateTime;
            sendf = true;
        }
        it.value().value = _val_;//冗余记录
        m_Map_Mutex.unlock();
        if(sendf){
//            qInfo() << QString("setValue:%1,%2").arg(it.value().id).arg(_val_,0,'g',2);
            setWDSVal(it.value().id, it.value().type, _val_);
        }
    }
}

void SerialPort::setWDSVal(int pid, PType type, float value)
{
//    qInfo() << QString("setWDSVal:%1,%2,%3").arg(pid).arg(type).arg(value);
    PTo _pto;
    if (mycachedata->getPTO(gatherID, pid, type, _pto))
    {
        WDS _wd(_pto.ptype, _pto.pid, value);
//        printf("setWDSVal:%d,%.2f\n", _pto.pid, value);
        ptr_ReceiveData->addWDS(_wd);
    }
}

void SerialPort::getMsgAndroid(QString msg,int pindex)
{
    if(serialconfig.name.toInt()==pindex){
//        uncodeMsg(msg,"01030000000AC5CD");
//        logNotify(QString("msg:%1,cmd:%2").arg(msg).arg(oldDownCmd));
//        msganalysisThread->AddMsg(msg,"01030000000AC5CD");//test
        msganalysisThread->AddMsg(msg,oldDownCmd);
    }
}

void SerialPort::readData()
{
#ifndef ANDROID
//    qInfo()<<"new recv be priming!";
    QByteArray data = serial->readAll();
    m_PortState.m_rf = true;
//    qInfo()<<data.toHex();
//    uncodeMsg(QString(data.toHex()),"01030000000AC5CD");
//    msganalysisThread->AddMsg(QString(data.toHex()),"01030000000AC5CD");//test
    msganalysisThread->AddMsg(QString(data.toHex()),oldDownCmd);
#else

#endif
}

void SerialPort::uncodeMsg(QString hexMsg,QString orederMsg)
{
    try{
        QScriptValueList args;
        args << hexMsg;
        args << orederMsg;
        myScr_Mutex.lock();
        QString retVal = myScr->myFunc("getReciveIDVAL",args).toString();
        myScr_Mutex.unlock();
    //    qInfo()<<"ret="<<retVal;
        if(retVal.isEmpty())
            return;
//        logNotify(retVal);
        QStringList idsDesc = retVal.split(';',QString::SkipEmptyParts);
        for(int i=0; i<idsDesc.size(); i++){
            QStringList ids = idsDesc.at(i).split(',',QString::SkipEmptyParts);
            if(2==ids.size()){
                int index = ids[0].toInt();
                float val = ids[1].toFloat();
                setVal(index,val);
            }
        }
    }catch(...){
        qDebug() << QString("SerialPort::uncodeMsg(%1,%2) exception").arg(hexMsg).arg(orederMsg);
    }
}

void SerialPort::TimeUp()
{
    unsigned int curT = mycachedata->getSec();
    if (m_PortState.PortStateTime<curT)
    {
        m_PortState.PortStateTime =curT+serialconfig.timePush;
        setWDSVal(-1, OnYX, m_PortState.m_bExit ? float(0.0) : float(1.0));
        setWDSVal(-2, OnYX, m_PortState.m_wf ? float(1.0) : float(0.0));
        setWDSVal(-3, OnYX, m_PortState.m_rf ? float(1.0) : float(0.0));
        setWDSVal(-4, OnYX, m_PortState.m_tc ? float(1.0) : float(0.0));
        if(!m_PortState.m_bExit){
            try{
                QMap<int,PInfo>::iterator it;
                for (it = addr_to_ids.begin();it != addr_to_ids.end();it++)
                {
                    bool sendf = false;
                    //间隔10秒上送
                    m_Map_Mutex.lock();
                    if ((it.value().sendTime + serialconfig.timePush)<curT)
                    {
                        it.value().sendTime = curT;
                        sendf = true;
                    }
                    m_Map_Mutex.unlock();
                    if(sendf){
                        setWDSVal(it.value().id, it.value().type, it.value().value);
                    }
                }
            }catch(...){
                qDebug() << "SerialPort::TimeUp for pstate send Exception";
            }
        }
    }
}

void SerialPort::sendMsg(QString cmd,bool u16f)
{
//    qInfo() << "cmd="<<cmd;
//    emit logNotify("16bit="+cmd);
    try{
#ifndef ANDROID
    if (!serial->isOpen())
    {
        openSerialPort();
        if (!serial->isOpen()){
            qWarning() << "send cmd fail,serial isn't open!";
            m_PortState.m_bExit=true;
            return;
        }
    }
    if (serial->isOpen()){
        m_PortState.m_bExit=false;
        if(u16f){
//            qInfo() << "cmd.size()="<<cmd.size()<<" cmd.length()="<<cmd.length();
            //16进制字符转10进制
            unsigned char buf[256] = { 0 };
            int rlen = PFunc::string2bytes(cmd.toStdString().c_str(), buf, cmd.length());
            serial->write((const char*)buf,rlen);

        }else {
            QByteArray data = cmd.toLocal8Bit();

//            qInfo() << "send cmd for serial write!";
//            qInfo() << "data.length="<<data.length();
            serial->write(data.data(),data.length());
        }
        if(serial->waitForBytesWritten()){
            oldDownCmd = cmd;
            m_PortState.m_wf=true;
        }else{
            qWarning() << "send cmd fail!";
            m_PortState.m_wf=false;
        }
    }
#else
//    qInfo() << QString("get cmd! cmd.size=%1").arg(cmd.size());
    if(!serialState)
    {
        openSerialPort();
        if (!serialState){
            qWarning() << "send cmd fail,serial isn't open!";
            m_PortState.m_bExit=true;
            return;
        }
    }
    if(serialState){
        m_PortState.m_bExit=false;
//        qInfo() << QString("send cmd for serial write! cmd.size=%1").arg(cmd.size());
//        emit logNotify(QString("gather(%1) send cmd for serial write! cmd.size=%2,cmd=%3")
//                       .arg(gatherID).arg(cmd.size()).arg(cmd));
        jint javaIndex = serialconfig.name.toInt();
        QAndroidJniObject javaMsg = QAndroidJniObject::fromString(cmd);
        jint ret = QAndroidJniObject::callStaticMethod<jint>("an/qt5/javagather/PL2303HXDSerialPort",
                                                  "WriteToUARTDevice"
                                                  ,"(ILjava/lang/String;)I"
                                                  ,javaIndex
                                                  , javaMsg.object<jstring>());
        QAndroidJniEnvironment env;
        if(env->ExceptionCheck()){
            qDebug()<<"SerialPort writeData Exception!";
            env->ExceptionClear();
            m_PortState.m_wf=false;
        }else{
            oldDownCmd = cmd;
            m_PortState.m_wf=true;
        }
        if(ret<0){
            m_PortState.m_wf=false;
            switch(ret){
            case -1:
                qDebug() << QString("gatherID(%1) WriteToUARTDevice fail,mSerialMulti=null").arg(gatherID);
                break;
            case -2:
                qDebug() << QString("gatherID(%1) WriteToUARTDevice fail,mSerialMulti is unconect").arg(gatherID);
                closeSerialPort();
                break;
            case -3:
                qDebug() << QString("gatherID(%1) WriteToUARTDevice fail,cmd is empty").arg(gatherID);
                break;
            case -4:
                qDebug() << QString("gatherID(%1) WriteToUARTDevice fail,UnsupportedEncodingException").arg(gatherID);
                break;
            case -5:
                qDebug() << QString("gatherID(%1) WriteToUARTDevice fail,fail to write").arg(gatherID);
                break;
            default:
                qDebug() << QString("gatherID(%1) WriteToUARTDevice fail,error unkown").arg(gatherID);
                break;
            }
        }
    }
#endif
    }catch(...){
        qDebug()<<QString("SerialPort::sendMsg(%1,%2) Exception!").arg(cmd).arg(u16f);
    }
}

#ifndef ANDROID
void SerialPort::handleError(QSerialPort::SerialPortError error)
{
    if (error == QSerialPort::ResourceError) {
        qWarning()<<tr("Critical Error")+":"+serial->errorString();
        closeSerialPort();
        m_PortState.m_bExit=true;
    }
}
#endif
void SerialPort::openSerialPort()
{
#ifndef ANDROID
    serial->setPortName(serialconfig.name);
    serial->setBaudRate(serialconfig.baudRate);
    serial->setDataBits(serialconfig.dataBits);
    serial->setParity(serialconfig.parity);
    serial->setStopBits(serialconfig.stopBits);
    serial->setFlowControl(serialconfig.flowControl);
    if (serial->open(QIODevice::ReadWrite)) {
        qInfo()<< tr("Connected to %1 : %2, %3, %4, %5, %6")
                   .arg(serialconfig.name)
                   .arg(serialconfig.stringBaudRate)
                   .arg(serialconfig.stringDataBits)
                   .arg(serialconfig.stringParity)
                   .arg(serialconfig.stringStopBits)
                   .arg(serialconfig.stringFlowControl);
    } else {
        qWarning()<<tr("Open %1 Error").arg(serialconfig.name)+":"+serial->errorString();
    }
#else
    jint javaIndex = serialconfig.name.toInt();
    jint javaBaudRate = (int)serialconfig.baudRate;
    jint javaReadCount = (int)serialconfig.readCount;
    jint javaReadSleep = (int)serialconfig.readSleep;
    jint ret = QAndroidJniObject::callStaticMethod<jint>("an/qt5/javagather/PL2303HXDSerialPort",
                                            "OpenUARTDevice",
                                            "(IIII)I",javaIndex,javaBaudRate,javaReadCount,javaReadSleep);
    QAndroidJniEnvironment env;
    if(env->ExceptionCheck()){
        qDebug()<<"openSerialPort Exception!";
        env->ExceptionClear();
    }
//    if(3==ret){
//        emit serialPortMaybeNoPermission();
//    }
    if(1!=ret){
        qDebug() << (QObject::tr("Open error,ret(%1),Index(%2)").arg(ret).arg(serialconfig.name));
        serialState = false;
    }else{
        serialState = true;
    }
#endif
}

void SerialPort::closeSerialPort()
{
#ifndef ANDROID
    if (serial->isOpen()){
        serial->close();
        qInfo()<<tr("Disconnected");
    }
#else
    if(serialState){
        jint javaIndex = serialconfig.name.toInt();
        QAndroidJniObject::callStaticMethod<void>("an/qt5/javagather/PL2303HXDSerialPort",
                                                  "closeUARTDevicel"
                                                  ,"(I)V"
                                                  ,javaIndex);
        serialState = false;
        QAndroidJniEnvironment env;
        if(env->ExceptionCheck()){
            qDebug()<<"closeSerialPort Exception!";
            env->ExceptionClear();
        }
    }
#endif
}
